﻿using System;
using System.Collections.Generic;
using System.Text;
using Vortex.IO;
using System.IO;
using Vortex.Debugging;

namespace Vortex.Drawing {

	/// <summary>
	/// Implementation of sprite font for Vortex2D.NET
	/// </summary>
	public class SpriteFont : Resource {
		public static readonly string DESC_FILE_SUFFIX = ".fnt";
		public static readonly string TEXTURE_FILE_SUFFIX = "_0.png";

		//global built-in fonts
		private static SpriteFont _defaultFont;
		private static SpriteFont _consoleFont;
		private static SpriteFont _smallConsoleFont;

		///<summary>Line height for this font</summary>
		private float _lineHeight;
		///<summary>Texture with glyphs (if font is based on external texture)</summary>
		private Texture _glyphTexture;
		///<summary>Array of characters</summary>
		private SpriteCharInfo[] _chars;

		#region Properties

		/// <summary>
		/// Gets the height of the text line for this line.
		/// </summary>
		/// <value>The height of the line.</value>
		public float LineHeight {
			get { return _lineHeight; }
		}

		/// <summary>
		/// Gets the <see cref="Vortex.Drawing.SpriteCharInfo"/> with the specified char code.
		/// </summary>
		/// <value><see cref="SpriteCharInfo"/> - information about char</value>
		public SpriteCharInfo this[char charCode] {
			get { int index = (int)charCode; return index < _chars.Length ? _chars[index] : null; }
		}

		/// <summary>
		/// Gets the default sprite font (Tahoma, 14px).
		/// </summary>
		/// <value>The default sprite font.</value>
		public static SpriteFont Default {
			get { return _defaultFont; }
		}

		/// <summary>
		/// Gets the default console font (Consolas, 16px).
		/// </summary>
		/// <value>The console font.</value>
		public static SpriteFont Console {
			get { return _consoleFont; }
		}

		/// <summary>
		/// Gets the small console font (Consolas, 14px) .
		/// </summary>
		/// <value>The small console font.</value>
		public static SpriteFont SmallConsole {
			get { return _smallConsoleFont; }
		}

		#endregion

		#region Constructors

		/// <summary>
		/// Initializes a new instance of the <see cref="SpriteFont"/> class from font base name.
		/// </summary>
		/// <param name="fontNameBase">
		/// The font name base. (It is common part for font desc and texture file names, e.g. @"tahoma" or @"fonts\consolas")
		/// </param>
		public SpriteFont(string fontNameBase)
			: this(fontNameBase + DESC_FILE_SUFFIX, fontNameBase + TEXTURE_FILE_SUFFIX) {
		}

		/// <summary>
		/// Initializes a new instance of the <see cref="SpriteFont"/> class from source files.
		/// </summary>
		/// <param name="fontDescFileName">Name of the font description (.fnt) file.</param>
		/// <param name="fontTextureFileName">Name of the font texture  file.</param>
		public SpriteFont(string fontDescFileName, string fontTextureFileName) {
			if (string.IsNullOrEmpty(fontTextureFileName)) throw new ArgumentException("Texture file name should be valid file path name", "fontTextureFileName");

			_glyphTexture = new Texture(fontTextureFileName, PixelFormat.DefaultAlpha);

			LoadFontDescription(fontDescFileName, _glyphTexture.ToSprite());
		}

		/// <summary>
		/// Initializes a new instance of the <see cref="SpriteFont"/> class from font description and existing sprite.
		/// </summary>
		/// <param name="fontDescFileName">Name of the font desc file.</param>
		/// <param name="glyphsSprite">The sprite which contains glyphs.</param>
		public SpriteFont(string fontDescFileName, Sprite glyphsSprite) {
			LoadFontDescription(fontDescFileName, glyphsSprite);
		}

		#endregion

		#region Internal stuff

		/// <summary>
		/// Encapsulates font description reading methods
		/// </summary>
		private class FontDescReader {
			private StringReader _reader;
			private string _line;

			/// <summary>
			/// Initializes a new instance of the <see cref="FontDescReader"/> class.
			/// </summary>
			/// <param name="reader">The string reader.</param>
			public FontDescReader(StringReader reader) {
				_reader = reader;
			}

			/// <summary>
			/// Reads the next line.
			/// </summary>
			public void ReadLine() {
				_line = _reader.ReadLine();
			}

			/// <summary>
			/// Gets the int value from line by key.
			/// </summary>
			/// <param name="keyName">Name of the key.</param>
			/// <returns>Integer value</returns>
			public int GetIntValue(string keyName) {
				return Int32.Parse(GetStringValue(keyName));
			}

			/// <summary>
			/// Gets the string value from line by key.
			/// </summary>
			/// <param name="keyName">Name of the key.</param>
			/// <returns>String value</returns>
			public string GetStringValue(string keyName) {
				int keyPos = _line.IndexOf(keyName + "=");
				if (keyPos >= 0) {
					string str = _line.Substring(keyPos + keyName.Length + 1);
					int separatorPos = str.IndexOf(" ");
					if (separatorPos >= 0) {
						return str.Substring(0, separatorPos);
					} else {
						return str;
					}
				}
				return "";
			}
		}

		/// <summary>
		/// Loads the font description file and binds all of characters to sprite.
		/// </summary>
		/// <param name="fontDescFileName">Name of the font desc file.</param>
		/// <param name="sprite">The glyph sprites.</param>
		private void LoadFontDescription(string fontDescFileName, Sprite sprite) {
			if (string.IsNullOrEmpty(fontDescFileName)) throw new ArgumentException("Font description file name should be valid file path name", "fontDescFileName");

			int maxCharId = 0;
			List<SpriteCharInfo> chars = new List<SpriteCharInfo>();

			string descFileContent = new ResourceFileInfo(fontDescFileName).GetContentAsText();
			using (StringReader stringReader = new StringReader(descFileContent)) {
				FontDescReader reader = new FontDescReader(stringReader);

				reader.ReadLine(); //skip header with unneccessary information
				reader.ReadLine(); //in this line we can get font height
				_lineHeight = (float)reader.GetIntValue("lineHeight");

				reader.ReadLine();
				reader.ReadLine();
				int charCount = reader.GetIntValue("chars count");
				

				for (int n = 0; n < charCount; ++n) {
					reader.ReadLine();

					int id = reader.GetIntValue("char id");
					int x = reader.GetIntValue("x"), 
						y = reader.GetIntValue("y"),
						width = reader.GetIntValue("width"),
						height = reader.GetIntValue("height"),
						xoffset = reader.GetIntValue("xoffset"),
						yoffset = reader.GetIntValue("yoffset"),
						xadvance = reader.GetIntValue("xadvance");

					if (id > maxCharId) { maxCharId = id; }

					Sprite glyphSprite = sprite.Cut(x, y, width, height).MoveOriginTo(-xoffset, -yoffset);

					chars.Add(new SpriteCharInfo((char)id, glyphSprite, new Vector2(xadvance, _lineHeight)));
				}

				//check max char id
				if (maxCharId >= (int)Char.MaxValue) {
					throw new OverflowException(string.Format("Maximal characted id = {0} is out of Unicode 16 address space", maxCharId));
				}

				//create char array
				_chars = new SpriteCharInfo[maxCharId + 1];
				//copy all characters to their places
				foreach (SpriteCharInfo charInfo in chars) {
					_chars[(int)charInfo.Code] = charInfo;
				}
			}
			
		}

		#endregion


        protected override void FreeData()
        {
            _glyphTexture.Dispose();
        }

		/// <summary>
		/// Loads the global content.
		/// </summary>
		internal static void LoadGlobals() {
			_defaultFont = new SpriteFont("Vortex.Drawing.Files.tahoma");
			_consoleFont = new SpriteFont("Vortex.Drawing.Files.consolas");
			_smallConsoleFont = new SpriteFont("Vortex.Drawing.Files.sconsolas");
		}

        internal static void FreeGlobals() {
            _defaultFont.Dispose();
            _consoleFont.Dispose();
            _smallConsoleFont.Dispose();
        }
	}
}
